#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"

#include "esp_log.h"
#include "cJSON.h"
#include "mqtt_client.h"

#include "aliot.h"
#include "aliot_mqtt.h"
#include "aliot_dm.h"
#include "nvs_manager.h"

#define TAG     "aliot_mqtt"

//mqtt连接客户端
static esp_mqtt_client_handle_t s_aliot_client = NULL;

//连接成功标志位
static bool s_aliot_connected_flg = false;


/**
 * mqtt连接事件处理函数
 * @param event 事件参数
 * @return 无
 */
void aliot_mqtt_event_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t )event_data;
    // esp_mqtt_client_handle_t client = event->client;

    switch((esp_mqtt_event_id_t )event_id) {
    case MQTT_EVENT_CONNECTED:              // 连接成功
        ESP_LOGI(TAG, "device mqtt connected!");
        s_aliot_connected_flg = true;
        break;
    case MQTT_EVENT_DISCONNECTED:           // 连接断开
        ESP_LOGI(TAG, "device mqtt disconnected!");
        s_aliot_connected_flg = false;
        break;
    case MQTT_EVENT_SUBSCRIBED:             // 收到订阅消息ACK
        ESP_LOGI(TAG, "device mqtt subscribed ack, msg_id = %d", event->msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED:           // 收到解订阅消息ACK
        break;
    case MQTT_EVENT_PUBLISHED:              // 收到发布消息ACK
        ESP_LOGI(TAG, "device mqtt published ack, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT error: %s", esp_err_to_name(event->error_handle->esp_tls_last_esp_err));
        break;
    default:
        break;
    }
}

/**
 * 获取连接参数
 * @param 无
 * @return mqtt连接参数
 */
static const esp_mqtt_client_config_t * aliot_mqtt_get_connect_client_cfg(void)
{
    static esp_mqtt_client_config_t s_mqtt_client = {0};
    char str_t[16];
    time_t t = time(0);
    static char client_id[128];
    static char username[128];
    static char str_password[33];
    char sign_content[256];
    char password[16];

    s_mqtt_client.broker.address.uri = "mqtts://"NVS_ALIOT_PRODUCT_KEY".iot-as-mqtt."NVS_ALIOT_REGION_ID".aliyuncs.com";
    s_mqtt_client.broker.address.port = 1883;

    //时间戳
    snprintf(str_t,16,"%lld000",t);

    //client id
    snprintf(client_id,sizeof(client_id),"%s|securemode=2,signmethod=hmacmd5,timestamp=%s|",aliot_get_clientid(),str_t);
    s_mqtt_client.credentials.client_id = client_id;

    //用户名
    snprintf(username,sizeof(username),"%s&%s",aliot_get_devicename(),NVS_ALIOT_PRODUCT_KEY);
    s_mqtt_client.credentials.username = username;

    //密码
    snprintf(sign_content,256,"clientId%sdeviceName%sproductKey%stimestamp%s",
    aliot_get_clientid(),aliot_get_devicename(),NVS_ALIOT_PRODUCT_KEY,str_t);
    calc_hmd5(aliot_get_devicesecret(),sign_content,password);
    core_hex2str(password,16,str_password,0);
    s_mqtt_client.credentials.authentication.password = str_password;

    ESP_LOGI(TAG,"aliot connect->clientId:%s,username:%s,password:%s",client_id,username,str_password);
    
    //CA根证书
    s_mqtt_client.broker.verification.certificate = g_aliot_ca;
    //s_mqtt_client.broker.verification.certificate_len = strlen(g_aliot_ca);

    return &s_mqtt_client;
}

/**
 * mqtt任务
 * @param 无
 * @return 无
 */
static void aliot_mqtt_run(void *args) 
{
    // 获取连接参数
    const esp_mqtt_client_config_t *mqtt_cfg = aliot_mqtt_get_connect_client_cfg();

    // mqtt客户端初始化
    s_aliot_client = esp_mqtt_client_init(mqtt_cfg);

    // mqtt客户端注册事件
    esp_mqtt_client_register_event(s_aliot_client, ESP_EVENT_ANY_ID, aliot_mqtt_event_handler, s_aliot_client);

    // mqtt客户端启动
    esp_mqtt_client_start(s_aliot_client);

    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒延迟（避免空转消耗CPU）
    }
}

/**
 * @brief aliot启动
 * @return esp_err_t 错误码
 */
esp_err_t aliot_mqtt_start(void)
{
    xTaskCreatePinnedToCore(aliot_mqtt_run, "aliot_run", 4096, NULL, 3, NULL, tskNO_AFFINITY);

    return ESP_OK;
}
